package com.googlecode.placesapiclient.client.service.impl;

import com.googlecode.placesapiclient.client.StatusCodeAnalyzer;
import com.googlecode.placesapiclient.client.argument.ArgumentMap;
import com.googlecode.placesapiclient.client.connection.proxy.ProxyConfiguration;
import com.googlecode.placesapiclient.client.entity.Place;
import com.googlecode.placesapiclient.client.entity.PlaceDetails;
import com.googlecode.placesapiclient.client.entity.PlacePrediction;
import com.googlecode.placesapiclient.client.exception.ErrorCode;
import com.googlecode.placesapiclient.client.exception.ErrorCodeException;
import com.googlecode.placesapiclient.client.parser.JSONParser;
import com.googlecode.placesapiclient.client.parser.impl.PlaceDetailsParser;
import com.googlecode.placesapiclient.client.parser.impl.PlaceListParser;
import com.googlecode.placesapiclient.client.parser.impl.PlacePredictionParser;
import com.googlecode.placesapiclient.client.service.PlacesService;
import com.googlecode.placesapiclient.client.service.ServiceName;
import com.googlecode.placesapiclient.client.util.Utils;
import org.apache.log4j.Logger;
import org.json.JSONObject;

import java.util.List;

import static com.googlecode.placesapiclient.client.util.Utils.getJsonObject;

/**
 */
public class PlacesServiceImpl implements PlacesService {

    private static final Logger logger = Logger.getLogger(PlacesServiceImpl.class.getName());

    private String apiKey;
    private JSONParser placeParser;
    private JSONParser placeDetailsParser;
    private JSONParser placePredictionParser;
    private ProxyConfiguration proxyConfiguration;

    /**
     *
     * @param apiKey
     *           - Your application's API key. This key identifies your application for purposes of quota management
     *           and so that Places added from your application are made immediately available to your app.
     *           Visit the https://code.google.com/apis/console/ to create an API Project and obtain your key.
     */
    public PlacesServiceImpl(String apiKey) {
        this.apiKey = apiKey;
    }

    @Override
    public void init() {
        // initalize parsers
        placeParser = new PlaceListParser();
        placeDetailsParser = new PlaceDetailsParser();
        placePredictionParser = new PlacePredictionParser();
    }

    @Override
    public List<Place> placeNearbySearchRequest(ArgumentMap argumentMap) throws ErrorCodeException {
        return execute(argumentMap, placeParser, ServiceName.PLACE_NEARBY_SEARCH_REQUEST);
    }

    @Override
    public List<Place> placeTextSearchRequest(ArgumentMap argumentMap) throws ErrorCodeException {
        return execute(argumentMap, placeParser, ServiceName.PLACE_TEXT_SEARCH_REQUEST);
    }

    @Override
    public List<Place> placeRadarSearchRequest(ArgumentMap argumentMap) throws ErrorCodeException {
        return execute(argumentMap, placeParser, ServiceName.PLACE_RADAR_SEARCH_REQUEST);
    }


    @Override
    public PlaceDetails placeDetailsRequest(ArgumentMap argumentMap) throws ErrorCodeException {
        return execute(argumentMap, placeDetailsParser, ServiceName.PLACE_DETAILS_REQUEST);
    }

    @Override
    public List<PlacePrediction> placeAutocompleteRequest(ArgumentMap argumentMap) throws ErrorCodeException {
        return execute(argumentMap, placePredictionParser, ServiceName.PLACE_AUTOCOMPLETE_REQUEST);
    }

    @Override
    public List<PlacePrediction> placeQueryAutocompleteRequest(ArgumentMap argumentMap) throws ErrorCodeException {
        return execute(argumentMap, placePredictionParser, ServiceName.PLACE_QUERY_AUTOCOMPLETE_REQUEST);
    }


    private <T> T execute(ArgumentMap argumentMap, JSONParser jsonParser, ServiceName serviceName) throws ErrorCodeException {
        try {
            JSONObject jsonObject = receiveResponse(serviceName.getServiceUrl(), argumentMap, serviceName);
            return jsonParser.parse(jsonObject);

        } catch (Throwable ex) {
            logger.error("UnexpectedException", ex);
            throw new ErrorCodeException(ErrorCode.UNKNOWN_EXCEPTION, "UnexpectedException", ex);
        }
    }

    private JSONObject receiveResponse(String baseUrl, ArgumentMap argumentMap, ServiceName serviceName) throws ErrorCodeException {
        String urlString  = Utils.makeUrl(baseUrl, argumentMap, serviceName);
        JSONObject jsonObject = getJsonObject(urlString, proxyConfiguration);
        StatusCodeAnalyzer.analyzeResponse(jsonObject);
        return jsonObject;
    }

    public ProxyConfiguration getProxyConfiguration() {
        return proxyConfiguration;
    }

    public void setProxyConfiguration(ProxyConfiguration proxyConfiguration) {
        this.proxyConfiguration = proxyConfiguration;
    }

    public void setApiKey(String apiKey) {
        this.apiKey = apiKey;
    }

    @Override
    public String getApiKey() {
        return apiKey;
    }
}